package MYAccumulators

import chisel3._
import chisel3.util._
import scala.math._
import MyIO._

// class AccumOut(size:Int) extends Bundle{
// 	//val dataOut= Output(SInt(32.W))//read data
// 	val waddr = Output(UInt(size.W))
// 	val wen = Output(Bool())
// 	val wclear = Output(Bool())
// 	val lastvec = Output(Bool())
// }


class MYAccumulator(addrWidth:Int) extends Module{
	val io = IO(new Bundle{
		val accumIn = new Setup_Accu(addrWidth)
		val raddr = Input(UInt(addrWidth.W))
		val dataIn = Input(SInt(32.W))
		
		val accumOut = Flipped(new Setup_Accu(addrWidth))
		val dataOut= Output(SInt(32.W))
	})

	val mem = SyncReadMem(pow(2, addrWidth).toInt, SInt(32.W))

	when(io.accumIn.wen){
		when(io.accumIn.wclear){
			mem.write(io.accumIn.waddr, io.dataIn)
		}. otherwise{
			mem.write(io.accumIn.waddr, io.dataIn + mem.read(io.accumIn.waddr))
		}
	}

	io.dataOut := mem.read(io.raddr)

	io.accumOut.waddr := RegNext(io.accumIn.waddr, 0.U)
	io.accumOut.wen   := RegNext(io.accumIn.wen,   false.B)
	io.accumOut.wclear:= RegNext(io.accumIn.wclear,false.B)
	io.accumOut.lastvec:= RegNext(io.accumIn.lastvec,false.B)

}

class MYAccumulators(addrWidth:Int, matrixSize:Int) extends Module{
	val io = IO(new Bundle{
		val accumsIn = new Setup_Accu(addrWidth)
		val datasIn = Input(Vec(matrixSize, SInt(32.W)))
		val raddr = Input(UInt(addrWidth.W))

		val datasOut = Output(Vec(matrixSize, SInt(32.W)))
		val done = Output(Bool())
	})

	//Accums
	private val Accums = Array.fill(matrixSize){
		Module(new MYAccumulator(addrWidth))
	}


	//connect the accum's input
	for(i <- 0 until matrixSize){
		if(i==0){//the first accum
			Accums(i).io.accumIn <> io.accumsIn
			Accums(i).io.raddr   := io.raddr
			Accums(i).io.dataIn  := io.datasIn(i)
		}else{//i>0
			Accums(i).io.accumIn <> Accums(i-1).io.accumOut
			Accums(i).io.raddr := io.raddr
			Accums(i).io.dataIn   := io.datasIn(i)
		}
	}
	//connct the accum's output
	for(i <- 0 until matrixSize){
		io.datasOut(i) := Accums(i).io.dataOut
	}

	io.done := Accums(matrixSize-1).io.accumOut.lastvec
}

// object AccumMain extends App{
// 	println("Generating the Accum hardware")
// 	emitVerilog(new MYAccumulator(8), Array("--target-dir", "generated"))
// }